﻿#include"XQHttpClient.h"
#include"QCoding.h"
#include"XQHttpHeadRequest.h"
#include"XQHttpHeadReply.h"
#include"XQLog.hpp"
#include<QDateTime>
#include<QLocalSocket>
#include<QNetworkRequest>
#include<QNetworkAccessManager>
#include<QNetworkReply>
#include<QEventLoop>
#include<QTimer>
#include<QDebug>
XQLog* XQHttpClient::m_log = XQLog::Create("XQHttpClient", LogType::Debug, XQLog::cmd_out | XQLog::cmd_cpp | XQLog::cmd_queue | XQLog::showTime);
XQHttpClient::XQHttpClient(NetworkType type, QObject* parent)
	:XQHttpObject(parent)
{
	m_networkType = type;
	init();
}

XQHttpClient::~XQHttpClient()
{
	if (m_request != nullptr)
		delete m_request;
	if (m_reply != nullptr)
		delete m_reply;
}

QString XQHttpClient::url() const
{
	return m_url;
}

bool XQHttpClient::get(const QString& url)
{
	/*if (m_debug)
		XQDebug << QString("Get\r\nurl:%1\r\n").arg(url);*/
	setUrl(url);
	return get();
}

bool XQHttpClient::get()
{
	m_request->setRequestType(HttpRequestType::GET);
	if (!connectToServerWait())
		return false;
	if (m_networkType == NetworkType::Tcp)
	{
		//开始发送请求
		if (m_tcpSocket->write(m_request->toByteArray()) == -1)
			return false;
	}
	else if (m_networkType == NetworkType::Local)
	{	
		//开始发送请求
		if (m_localSocket->write(m_request->toByteArray()) == -1)
			return false;
	}
	return true;
}

bool XQHttpClient::post(const QString& url, const QByteArray& data)
{
	/*if(m_debug)
		XQDebug<<QString("Post\r\nurl:%1\r\ndata:\r\n%2").arg(url).arg(data);*/
	setUrl(url);
	return post(data);
}

bool XQHttpClient::post(const QByteArray& data)
{
	QIODevice* Socket = nullptr;
	if (isTcpNetwork())
		Socket = m_tcpSocket;
	else if (isLocalNetwork())
		Socket = m_localSocket;
	if (!connectToServerWait())
		return false;
	if (Socket == nullptr)
		return false;
	m_request->setRequestType(HttpRequestType::POST);
	m_request->setContent(data);
	auto len = Socket->write(m_request->toByteArray());
	//开始发送请求
	if (len == -1)
		return false;
	return true;
}

bool XQHttpClient::post(const QString& url, const QJsonDocument& json)
{
	m_request->setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
	return post(url, json.toJson());
}

bool XQHttpClient::wait_Reply(int timeout)
{
	QDateTime start = QDateTime::currentDateTime();
	bool flag = true;
	QEventLoop loop;
	connect(this, &XQHttpClient::headReplyRead, &loop, &QEventLoop::quit);
	if(timeout>0)
	{
		QTimer::singleShot(timeout, &loop, &QEventLoop::quit);//超时退出
	}
	loop.exec();
	if (start.msecsTo(QDateTime::currentDateTime()) >= timeout)
		flag = false;
	return flag;
}

void XQHttpClient::sendSignals(const QString& name, const QVariantList& list)
{
	QJsonDocument jsonDoc= QVariantList_toJson(list);
	QIODevice* Socket = nullptr;
	if (isTcpNetwork())
	{
		if(m_tcpSocket->state() != QAbstractSocket::ConnectedState)
			return;
		Socket = m_tcpSocket;
	}
	else if (isLocalNetwork())
	{
		if (m_localSocket->state() != QAbstractSocket::ConnectedState)
			return;
		Socket = m_localSocket;
	}
	if (Socket == nullptr)
		return;
	XQHttpHeadRequest request;
	request.setHeader(QNetworkRequest::ContentTypeHeader, SignalsRequestHeader);
	request.setHeader(SignalsRequestHeader, name.toUtf8());
	request.setUrl(url().toUtf8());
	request.setRequestType(HttpRequestType::POST);
	request.setContent(jsonDoc.toJson());
	auto byte = request.toByteArray();
	Socket->write(request.toByteArray());
}

void XQHttpClient::setServerName_local(const QString& name)
{
	if (!(m_networkType == NetworkType::Local))
		return;
	m_localSocket->setServerName(name);
}

void XQHttpClient::connectToServer(QIODeviceBase::OpenMode openMode)
{
	if (isTcpNetwork())
		m_tcpSocket->connectToHost(m_request->host(),m_request->port(), openMode);
	else if(isLocalNetwork())
		m_localSocket->connectToServer(m_request->host(),openMode);
}

void XQHttpClient::connectToServer(const QString& url, QIODeviceBase::OpenMode openMode)
{
	setUrl(url);
	connectToServer(openMode);
}

bool XQHttpClient::connectToServerWait(const QString& url,int msecs)
{
	setUrl(url);
	return connectToServerWait(msecs);
}

bool XQHttpClient::connectToServerWait(int msecs)
{
	if (isTcpNetwork())
	{
		if (!(m_tcpSocket->state() == QAbstractSocket::ConnectedState && m_tcpSocket->peerName() == m_request->host() && m_tcpSocket->peerPort() == m_request->port()))
		{
			if (!m_tcpSocket->state() == QAbstractSocket::UnconnectedState)
			{
				m_tcpSocket->disconnected();
				if (!m_tcpSocket->waitForDisconnected(msecs))//断开上次的连接
					return false;
			}
			//qDebug() << m_request->host() << m_request->port();
			if (url().startsWith("https", Qt::CaseInsensitive))
				m_tcpSocket->connectToHostEncrypted(m_request->host(), m_request->port());//开始建立新连接
			else
				m_tcpSocket->connectToHost(m_request->host(), m_request->port());//开始建立新连接
			if (!m_tcpSocket->waitForConnected(msecs))
			{
				if (isDebugModel(Http::Debug::error))
					XQHttpClientLog << m_tcpSocket->error() << m_tcpSocket->errorString();
				return false;
			}
		}
	}
	else if (isLocalNetwork())
	{
		if (!(m_localSocket->state() == QLocalSocket::ConnectedState && m_localSocket->serverName() == m_request->host()))
		{
			if (!m_localSocket->state() == QLocalSocket::UnconnectedState)
			{
				m_localSocket->disconnected();
				if (!m_localSocket->waitForDisconnected(msecs))//断开上次的连接
					return false;
			}
			m_localSocket->connectToServer(m_request->host());//开始建立新连接
			if (!m_localSocket->waitForConnected(msecs))
				return false;
		}
	}
	return true;
}

void XQHttpClient::waitForConnected_local(int msecs)
{
	if (!(m_networkType == NetworkType::Local))
		return;
	m_localSocket->waitForConnected(msecs);
}

void XQHttpClient::close()
{
	if (isTcpNetwork()&& m_tcpSocket->state() == QAbstractSocket::ConnectedState)
		m_tcpSocket->disconnected();
	else if (isLocalNetwork()&& m_localSocket->state() == QLocalSocket::ConnectedState)
		m_localSocket->disconnected();
}

void XQHttpClient::clear_request()
{
	m_request->clear();
}

XQHttpHeadRequest* XQHttpClient::request() const
{
	return m_request;
}

XQHttpHeadReply* XQHttpClient::reply() const
{
	return m_reply;
}

QString XQHttpClient::toString()const
{
	return toByteArray();
}

QByteArray XQHttpClient::toByteArray() const
{
	return m_reply->content();
}

QVariantList XQHttpClient::toList() const
{
	return QJsonDocument::fromJson(toByteArray()).toVariant().toList();
}

QByteArray XQHttpClient::Get(NetworkType type, const QString& url, int timeout, bool toUtf8)
{
	XQHttpClient http(type);
	if (!http.get(url))
		return QByteArray();
	if (!http.wait_Reply(timeout))
		return QByteArray();
	QByteArray ret = http.toByteArray();
	if (toUtf8)
		ret = http.GbkToUtf8(ret);
	return ret;
}

QByteArray XQHttpClient::Get_Tcp(const QString& url, int timeout, bool toUtf8)
{
	return Get(NetworkType::Tcp,url,timeout,toUtf8);
}

QByteArray XQHttpClient::Get_Local(const QString& url, int timeout, bool toUtf8)
{
	return Get(NetworkType::Local, url, timeout, toUtf8);
}

bool XQHttpClient::Post(NetworkType type, const QString& url, const QByteArray& data, int timeout)
{
	XQHttpClient http(type);
	if (!http.post(url, data))
		return false;
	if (!http.wait_Reply(timeout))
		return false;
	return true;
}

bool XQHttpClient::Post(NetworkType type, const QString& url, const QJsonDocument& json, int timeout)
{
	XQHttpClient http(type);
	if (!http.post(url, json))
		return false;
	if (!http.wait_Reply(timeout))
		return false;
	return true;
}

QByteArray XQHttpClient::Post_Reply(NetworkType type, const QString& url, const QByteArray& data, int timeout, bool toUtf8)
{
	XQHttpClient http(type);
	if (!http.post(url, data))
		return QByteArray();
	if (!http.wait_Reply(timeout))
		return QByteArray();
	QByteArray ret = http.toByteArray();
	if (toUtf8)
		ret = http.GbkToUtf8(ret);
	return ret;
}

QByteArray XQHttpClient::Post_Reply(NetworkType type, const QString& url, const QJsonDocument& json, int timeout, bool toUtf8)
{
	XQHttpClient http(type);
	if (!http.post(url, json))
		return QByteArray();
	if (!http.wait_Reply(timeout))
		return QByteArray();
	QByteArray ret = http.toByteArray();
	if (toUtf8)
		ret = http.GbkToUtf8(ret);
	return ret;
}

bool XQHttpClient::Post_Tcp(const QString& url, const QByteArray& data, int timeout)
{
	return Post(NetworkType::Tcp,url,data, timeout);
}

bool XQHttpClient::Post_Tcp(const QString& url, const QJsonDocument& json, int timeout)
{
	return Post(NetworkType::Tcp, url, json, timeout);
}

bool XQHttpClient::Post_Local(const QString& url, const QByteArray& data, int timeout)
{
	return Post(NetworkType::Local, url, data, timeout);
}

bool XQHttpClient::Post_Local(const QString& url, const QJsonDocument& json, int timeout)
{
	return Post(NetworkType::Local, url, json, timeout);
}

void XQHttpClient::setUrl(const QString& url)
{
	m_url = url;
	m_request->setUrl(url.toUtf8());
}

void XQHttpClient::init()
{
	if (m_request == nullptr)
		m_request = new XQHttpHeadRequest();
	if (m_reply == nullptr)
		m_reply = new XQHttpHeadReply();
	if (m_networkType == NetworkType::Tcp)
	{
		if(m_tcpSocket == nullptr)
		{
			m_tcpSocket = new QSslSocket(this);
			QSslConfiguration sslConfig = m_tcpSocket->sslConfiguration();
			sslConfig.setProtocol(QSsl::AnyProtocol);
			m_tcpSocket->setSslConfiguration(sslConfig);
			connect(m_tcpSocket, &QTcpSocket::readyRead, this, &XQHttpClient::readyRead);
			connect(m_tcpSocket, &QTcpSocket::connected, this, &XQHttpClient::readyConnected);
			connect(m_tcpSocket, &QTcpSocket::disconnected, this, &XQHttpClient::readyDisconnected);
			connect(m_tcpSocket, &QTcpSocket::errorOccurred, [this] {if (m_debug & Http::Debug::error)XQHttpClientLog <<m_tcpSocket->errorString(); });
		}
		if (m_localSocket)
		{
			m_localSocket->deleteLater();
			m_localSocket = nullptr;
		}
	}
	else if (m_networkType == NetworkType::Local)
	{
		if(m_localSocket == nullptr)
		{
			m_localSocket = new QLocalSocket(this);
			connect(m_localSocket, &QLocalSocket::readyRead, this, &XQHttpClient::readyRead);
			connect(m_localSocket, &QLocalSocket::connected, this, &XQHttpClient::readyConnected);
			connect(m_localSocket, &QLocalSocket::disconnected, this, &XQHttpClient::readyDisconnected);
			connect(m_localSocket, &QLocalSocket::errorOccurred, [this] {if (m_debug & Http::Debug::error)XQHttpClientLog << m_localSocket->errorString(); });
		}
		if (m_tcpSocket)
		{
			m_tcpSocket->deleteLater();
			m_tcpSocket = nullptr;
		}
	}
}


void XQHttpClient::readyRead()
{
	QIODevice* socket = nullptr;
	if (isTcpNetwork())
		socket = m_tcpSocket;
	else if(isLocalNetwork())
		socket = m_localSocket;
	if (socket == nullptr)
		return;
	//auto buff = Socket->readAll();
	m_buffer.append(socket->readAll());

	//判断响应头是否完整
	auto nSel = m_buffer.indexOf("\r\n\r\n");
	while (nSel != -1)
	{
		auto startIndex = nSel + 4;//内容开始的索引
		if (!m_replyAccept)
		{
			//解析请求头和内容
			m_reply->clear();
			m_reply->setData(m_buffer.left(startIndex));
			m_replyAccept = true;
		}
		auto size = m_reply->contentLength() - m_reply->buffSize();//剩余大小
		m_reply->setBuffer(m_buffer.mid(startIndex, size));//
		m_buffer.remove(startIndex, size);//从缓存中去除这个内容
		//调用函数处理内容
		if(m_buffFunc)
			m_buffFunc(socket,m_reply);
		emit buffRead(socket, m_reply);
		//判断内容是否完全读取处理完成
		if (!m_reply->isBuffAcceptFinish())
			return;//内容不完全继续等待
		//请求头清理
		m_buffer.remove(0,startIndex);

		emit headReplyRead(*m_reply);
		auto& content = m_reply->content();
		if (ishandlingSignalsRequest(m_reply))
		{
			auto ret = handlingSignalsRequest(m_reply, content);
			if (!ret.isEmpty())
				socket->write(ret);
		}
		nSel = m_buffer.indexOf("\r\n\r\n");
		m_replyAccept = false;
	}
	
}
void XQHttpClient::readyConnected()
{
	
	if (m_debug & Http::Debug::Connection)
	{
		QString info;
		if (isTcpNetwork() && m_tcpSocket)
			info = QString("\tTcp模式\t连接上服务器 %1").arg(hostPort(m_tcpSocket));
		else if (isLocalNetwork() && m_localSocket)
			info = QString("\tLocal模式\t连接上服务器 %1").arg(m_request->host());
		XQHttpClientLog << info;
	}
	emit connected();
}

void XQHttpClient::readyDisconnected()
{
	emit disconnected();
	if (m_debug & Http::Debug::Disconnect)
	{
		if (isTcpNetwork() && m_tcpSocket)
			XQHttpClientLog << "\tTcp模式\t与服务器连接断开" << hostPort(m_tcpSocket);
		else if (isLocalNetwork())
			XQHttpClientLog << "\tLocal模式\t与服务器连接断开";
	}
}
